#include <sys/socket.h>     // 包含socket()、bind()、connect()等函数声明
#include <netinet/in.h>     // 包含AF_INET、SOCK_STREAM等常量定义
#include <arpa/inet.h>      // 包含inet_addr()等IP地址处理函数（如果需要）
#include <unistd.h>         // 包含close()函数
//#include <error.h>
//#include <errno.h>
#include <stdlib.h>

#include <cstdio>
#include <cstring>          // 提供std::strerror
#include <string>

using std::string;

#define BUFFER_SIZE 1024

inline bool ret_check(int ret, const string& msg = "", bool should_exit = true)
    { 
        if(ret < 0){
            char message[256];
            
            snprintf(message, sizeof(message), 
                     "%s | %d | %s | error : %s\n | errno = %d(%s) \n", 
                     (const char*)__FILE__, __LINE__, __FUNCTION__, msg.c_str(), errno, std::strerror(errno));
            perror(message);
            
            if(should_exit)
            {
                exit(EXIT_FAILURE);
            }
            return false;
        } 
        return true;
    }

bool test(){
    int ret = 0;
    
    // 1. 创建套接字(与服务器相同)
    int socket_fd = socket(AF_INET, SOCK_STREAM, 0);
    ret_check(socket_fd, "socket failed");

    // 2. 连接到服务器（替换bind和listen）
    struct sockaddr_in server_addr = {0};
    server_addr.sin_family = AF_INET;
    server_addr.sin_addr.s_addr = inet_addr("127.0.0.1");   // 服务器IP（必须指定
    server_addr.sin_port = htons(8080);                     // 服务器端口
    ret = connect(socket_fd, (struct sockaddr*)&server_addr, sizeof(server_addr));
    ret_check(ret, "connect failed");

    printf("Connected to server at 127.0.0.1:8080 \n");
    printf("Enter message to send (ctrl+c to exit): \n");

    // 3. 多路复用：同时监控stdin(键盘输入)和socket_fd(服务器消息)
    fd_set read_fds;
    char send_buf[BUFFER_SIZE] = {0};
    char recv_buf[BUFFER_SIZE] = {0};
    
    while(true)
    {
        // 初始化监控集合（每次循环需重新设置，因为select会修改集合）
        FD_ZERO(&read_fds);
        FD_SET(STDIN_FILENO, &read_fds);    // 监控stdin(文件描述符0)
        FD_SET(socket_fd, &read_fds);       // 监控socket_fd 

        // 确定最大文件描述符(select第一个参数需要)
        int max_fd = (socket_fd > STDIN_FILENO) ? socket_fd : STDIN_FILENO;

        // 等待事件（超时设为NULL，一直阻塞直到有事件）
        ret = select(max_fd + 1, &read_fds, NULL, NULL, NULL);
        ret_check(ret, "select failed");
        
        // 4.1 若stdin可读（用户输入了内容）
        if(FD_ISSET(STDIN_FILENO, &read_fds))
        {
            if(fgets(send_buf, BUFFER_SIZE, stdin) == NULL)
            {
                break;
            }
            // 发送到服务器
            ret = send(socket_fd, send_buf, strlen(send_buf), 0);     // 不需要修改send的文件描述符，正确做法是同时监控socket_fd和stdin
            ret_check(ret, "send failed");
            printf("Sent: %s\n", send_buf);
            memset(send_buf, 0, BUFFER_SIZE);   // 清空缓冲区
        }


        // 4.2 若socket_fd可读（服务器发送了消息）
        if(FD_ISSET(socket_fd, &read_fds))
        {
            ret = recv(socket_fd, recv_buf, BUFFER_SIZE, 0);
            if(ret <= 0)
            {
                printf("\nServer disconnected\n");
                break;
            }
            // 显示到屏幕（注意：recv不会自动加换行，需要手动添加）
            printf("Received: %s\n", recv_buf);
            memset(recv_buf, 0, BUFFER_SIZE);
            
            printf("Enter message to send: \n");    // 提示用户继续输入
        }
    }

    close(socket_fd);
    return 0;
}

int main()
{
    test();
    return 0;
}

